/*
 * Students:
 * Massimiliano Rango - 610826
 * Martina Benedetto - 610962
 * 
 * A.A 2011/2012
 * 
 */ 

import java.util.Random;

/**
 *
 * This class represents a node which uses the LSM protocol to detect if there is
 * a clone in the network.
 * 
 * @version 1.0 27 Dec 2011
 * @author Massimiliano Rango
 *
 */
public class LSMNode extends Node {
	
	/**
	 * Creates a new LSMNode with the given ID, position in the network and
	 * the configuration parameters
	 * @param ID Integer representing the ID of the node in the network
	 * @param pos Position of the node in the newtork
	 * @param conf Configuration object containing the parameters of the 
	 * simulation
	 */
	public LSMNode(int ID, Position pos, Configuration conf) {
		super(ID, pos, conf);
	}
	
	/**
	 * Creates a Position with two float random values between 0 and 1
	 * @return A random Position value
	 */
	private Position getRandomPos() {
		Random rand_p = new Random();
		return new Position(rand_p.nextFloat(), rand_p.nextFloat());
	}
	
	public void run() {
		try {
			for (int i = 0; !isKilled() && i < sizeNeighbours();) {
				if (sendMessage(new LocationClaim(getID(), getPosition()), getNeighbour(i), true) 
						!= null) {
					i++;
				}
			}
			m_conf.addNodeLocClaim();	/* this node has had a chance to send its location claims */
			if (!isKilled()) {
				synchronized (m_conf.lockEndSimulation) {
					while (m_conf.getNodeLocClaim() < m_conf.getNNodes() + 1) {
						m_conf.lockEndSimulation.wait();
					}
				}
				while (!isKilled() && !m_conf.getCloneFound() && m_conf.getCirculatingMess() > 0) {
					Message rec = null;
					synchronized (lockIncoming) {
						while (sizeIncoming() == 0 && m_conf.getCirculatingMess() > 0 
								&& !m_conf.getCloneFound()) {
							lockIncoming.wait();
						}
					}
					
					/* 
					 * This node wakes up if another node sent him a message 
					 * or the hypervisor discovered that there are no message circulating.
					 */
					if (m_conf.getCirculatingMess() > 0 && !m_conf.getCloneFound()) {
						rec = receiveMessage();
					}
					if (!isKilled() && rec != null) { 
						
						/* This node has not died while receiving the message */
						if (rec instanceof LocationClaim) {
							Random r = new Random();
							float random = r.nextFloat();
							if (random < m_conf.getProb()) {
								for (int i = 0; !isKilled() && i < m_conf.getG(); i++) {
									Position finalDest = getRandomPos();
									ControlMessage cm = new ControlMessage(rec.getID(), 
											rec.getPosition(), finalDest);
									addArchive(cm);
									cloneDetection(true);
									if (!m_conf.getCloneFound() && !isKilled()) {
										Node nearest = nearest(finalDest);
										while (!sendToNearest(cm, nearest, true)) {
											nearest = nearest(finalDest);
										}
									}
								}
							}
							m_conf.removeCirculatingMess(1);	/* The incoming location claim has been processed */
						} else if (rec instanceof ControlMessage) {
							ControlMessage cm = (ControlMessage)rec;
							addArchive(cm);
							cloneDetection(false);
							if (!m_conf.getCloneFound() && !isKilled()) {
								Node nearest = nearest(cm.getDestination());
								while (!sendToNearest(cm, nearest, false)) {
									nearest = nearest(cm.getDestination());
								}
							}
						}
					}
				}
			}
		} catch (InterruptedException e) {
			die();
			m_conf.addKilledNode();
			return;
		}
		setKilled();
		m_conf.addKilledNode();
	}			
}
